package accounts

import (
	"errors"
	"gitee.com/slion/account/services"
	"gitee.com/slion/helper"
	"gitee.com/slion/infra/base"
	"github.com/shopspring/decimal"
	"github.com/sirupsen/logrus"
	"github.com/tietang/dbx"
)

// 业务领域层(整个业务的核心层)

// 有状态的,每次调用都要实例化
type DomainAccount struct {
	account Account
	accountLog AccountLog
}

// 创建logNo
func (domain *DomainAccount) createAccountLogNo(){
	domain.accountLog.LogNo = helper.GetKsuid()
}

// 创建accountNo
func (domain *DomainAccount) createAccountNo(){
	domain.account.AccountNo = helper.GetKsuid()
}

// 创建流水记录
func (domain *DomainAccount) createAccountLog(){
	// 通过account来创建流水 (创建账户,再创建流水)
	domain.accountLog = AccountLog{}
	domain.createAccountLogNo()
	domain.accountLog.TradeNo = domain.accountLog.LogNo
	// 流水中交易主体信息
	domain.accountLog.AccountNo = domain.account.AccountNo
	domain.accountLog.UserId = domain.account.UserId
	domain.accountLog.Username = domain.account.Username
	// 交易对象信息(创建账户的交易目标也是自身)
	domain.accountLog.TargetAccountNo = domain.account.AccountNo
	domain.accountLog.TargetUserId = domain.account.UserId
	domain.accountLog.TargetUsername = domain.account.Username
	// 交易金额(交易金额和余额都是创建账户时的余额)
	domain.accountLog.Amount = domain.account.Balance
	domain.accountLog.Balance = domain.account.Balance
	// 交易变化属性
	domain.accountLog.Decs = "创建账户"
	domain.accountLog.ChangeType = services.AccountCreated
	domain.accountLog.ChangeFlag = services.FlagAccountCreated
	domain.accountLog.Status = 1
}

// 账户创建
// param: dto services.AccountDTO 账户信息
func (domain *DomainAccount) Create(dto *services.AccountDTO) (*services.AccountDTO, error) {

	// 创建账户持久化对象
	domain.account = Account{}
	// 调用FromDTO()填充Account实体类
	domain.account.FromDTO(dto)
	domain.createAccountNo()

	// 创建账户流水持久化对象
	// 调用createAccountLog填充AccountLog实体类
	domain.createAccountLog()

	// 创建数据库访问层对象
	accountDao := AccountDao{}
	accountLogDao := AccountLogDao{}

	db := base.DbxDatabase()
	err := db.Tx(func(run *dbx.TxRunner) error {

		accountDao.runner = run
		accountLogDao.runner = run

		// 插入账户数据
		id, err := accountDao.Insert(&domain.account)
		if err != nil{
			return err
		}
		if id <= 0 {
			return errors.New("创建账户失败")
		}
		// 插入账户流水数据
		id, err = accountLogDao.Insert(&domain.accountLog)
		if err != nil{
			return err
		}
		if id <= 0 {
			return errors.New("创建账户流水失败")
		}
		return nil
	})
	if err != nil{
		logrus.Error(err)
		return nil, err
	}
	return domain.account.ToDTO(), nil
}

// 转账
func (domain *DomainAccount) Transfer(dto services.AccountTransferDTO) (services.TransferStatus, error){

	// 如果交易类型是支出
	amount := dto.Amount
	if dto.ChangeFlag == services.FlagExpenditure {
		// amount变为负数
		amount = amount.Mul(decimal.NewFromFloat(-1))
	}

	// 1. 创建账户流水记录
	domain.accountLog = AccountLog{}
	domain.accountLog.FromTransferDTO(dto)
	domain.createAccountLogNo()

	// 2. 检测余额是否足够和更新余额
	accountDao := &AccountDao{}
	accountLogDao := &AccountLogDao{}

	// 默认状态(转账失败)
	status := services.TransferStatusFailure

	db := base.DbxDatabase()
	err := db.Tx(func(run *dbx.TxRunner) error {
		accountDao.runner = run
		accountLogDao.runner = run

		// 更新余额
		rows, err := accountDao.UpdateBalance(dto.TradeBody.AccountNo, amount)
		if err != nil{
			return err
		}
		// rows小于等于0 且 交易标识为支出, 说明余额不足
		if rows <= 0 && dto.ChangeFlag == services.FlagExpenditure{
			status = services.TransferStatusInsufficientBalance // 余额不足
			return errors.New("余额不足")
		}
		// 查询最新的账户信息
		accountInfo := accountDao.GetOne(dto.TradeBody.AccountNo)
		// 账户日志的Balance 等于 账户的Balance
		domain.accountLog.Balance = accountInfo.Balance
		// 插入账户流水
		rows, err = accountLogDao.Insert(&domain.accountLog)
		if err != nil || rows < 0 {
			return errors.New("账户流水创建失败")
		}
		return nil
	})

	if err != nil{
		logrus.Error(err)
		return status, err
	}
	return services.TransferStatusSuccess, nil
}

// 根据账户编号查询账户信息
func (domain *DomainAccount) GetAccount(accountNo string) *services.AccountDTO {

	account := &Account{}
	accountDao := AccountDao{}
	db := base.DbxDatabase()
	err := db.Tx(func(run *dbx.TxRunner) error {
		accountDao.runner = run
		account = accountDao.GetOne(accountNo)
		return nil
	})
	if err != nil {
		return nil
	}
	if account == nil {
		return nil
	}
	return account.ToDTO()
}

// 根据用户id和账户类型来查询账户信息
func (domain *DomainAccount) GetAccountByUserId(userId string, accountType int) *services.AccountDTO{

	account := &Account{}
	accountDao := AccountDao{}
	db := base.DbxDatabase()
	err := db.Tx(func(run *dbx.TxRunner) error {
		accountDao.runner = run
		account = accountDao.GetByUserId(userId, accountType)
		return nil
	})
	if err != nil {
		return nil
	}
	if account == nil {
		return nil
	}
	return account.ToDTO()
}

// 根据流水编号查询账户流水
func (domain *DomainAccount) GetAccountLog(logNo string) *services.AccountLogDTO{

	accountLog := &AccountLog{}
	accountLogDao := AccountLogDao{}
	db := base.DbxDatabase()
	err := db.Tx(func(run *dbx.TxRunner) error {
		accountLogDao.runner = run
		accountLog = accountLogDao.GetOne(logNo)
		return nil
	})
	if err != nil {
		return nil
	}
	if accountLog == nil {
		return nil
	}
	return accountLog.ToDTO()
}

// 根据交易编号查询账户流水
func (domain *DomainAccount) GetAccountLogByTradeNo(tradeNo string) *services.AccountLogDTO{

	accountLog := &AccountLog{}
	accountLogDao := AccountLogDao{}
	db := base.DbxDatabase()
	err := db.Tx(func(run *dbx.TxRunner) error {
		accountLogDao.runner = run
		accountLog = accountLogDao.GetByTradeNo(tradeNo)
		return nil
	})
	if err != nil {
		return nil
	}
	if accountLog == nil {
		return nil
	}
	return accountLog.ToDTO()
}
